/*
 * Created on 2005-9-5
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package com.hitrust.gs.util.security;

/**
 * @author HP
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class Base64 {
    /**
     * Byte value that maps to 'a' in Base64 encoding
     */
   private final static int LOWER_CASE_A_VALUE = 26;   

   /**
     * Byte value that maps to '0' in Base64 encoding
     */
   private final static int ZERO_VALUE = 52;

   /**
     * Byte value that maps to '+' in Base64 encoding
     */
   private final static int PLUS_VALUE = 62;

   /**
     * Byte value that maps to '/' in Base64 encoding
     */
   private final static int SLASH_VALUE = 63;

   /**
     * Bit mask for one character worth of bits in Base64 encoding.
     * Equivalent to binary value 111111b.
     */
   private final static int SIX_BIT_MASK = 63;

   /**
     * Bit mask for one byte worth of bits in Base64 encoding.
     * Equivalent to binary value 11111111b.
     */
   private static final int EIGHT_BIT_MASK = 0xFF;

   /**
    * The input String to be decoded
    */
   private String mString;
   
   /**
    * Current position in the String(to be decoded)
    */
   private int mIndex = 0;
   
   /**
     * Encode an array of bytes using Base64
     * @param data[] The bytes to be encoded
     * @return A valid Base64 representation of the input
     */ 
   public String encode( byte data[] ) {
       // Base64 encoding yields a String that is 33% longer than the byte array
       int charCount = ( ( data.length * 4 ) / 3 ) + 4;
       
       // New lines will also be needed for every 76 charactesr, so allocate a
       // StringBuffer that is long enough to hold the full result without
       // having to expand later
       StringBuffer result = new StringBuffer( ( charCount * 77 ) / 76 );
       
       int byteArrayLength = data.length;
       int byteArrayIndex = 0;
       int byteTriplet = 0;
       while ( byteArrayIndex < byteArrayLength - 2 ) {
           // Build the 24 bit byte triplet from the input data
           byteTriplet = convertUnsignedByteToInt( data[ byteArrayIndex++ ] );
           // Each input byte contributes 8 bits to the triplet
           byteTriplet <<= 8;
           byteTriplet |= convertUnsignedByteToInt( data[ byteArrayIndex++ ] );
           byteTriplet <<= 8;
           byteTriplet |= convertUnsignedByteToInt( data[ byteArrayIndex++ ] );

           // Look at the lowest order six bits and remember them
           byte b4 = (byte)( SIX_BIT_MASK & byteTriplet );
           // Move the byte triplet to get the next 6 bit value
           byteTriplet >>= 6;
           byte b3 = (byte)( SIX_BIT_MASK & byteTriplet );
           byteTriplet >>= 6;
           byte b2 = (byte)( SIX_BIT_MASK & byteTriplet );
           byteTriplet >>= 6;
           byte b1 = (byte)( SIX_BIT_MASK & byteTriplet );
           
           // Add the Base64 encoded character to the result String
           result.append( mapByteToChar( b1 ) );
           result.append( mapByteToChar( b2 ) );
           result.append( mapByteToChar( b3 ) );
           result.append( mapByteToChar( b4 ) );

           // There are 57 bytes for every 76 characters, so wrap the line when needed
           //if ( byteArrayIndex % 57 == 0 ) {
           //    result.append( "\n" );
           //}
       }
       
       // Check if we have one byte left over
       if ( byteArrayIndex == byteArrayLength - 1 ) {
           // Convert our one byte to an int
           byteTriplet = convertUnsignedByteToInt( data[ byteArrayIndex++ ] );
           // Right pad the second 6 bit value with zeros
           byteTriplet <<= 4;
           
           byte b2 = (byte)( SIX_BIT_MASK & byteTriplet );
           byteTriplet >>= 6;
           byte b1 = (byte)( SIX_BIT_MASK & byteTriplet );
           
           result.append( mapByteToChar( b1 ) );
           result.append( mapByteToChar( b2 ) );
           
           // Add "==" to the output to make it a multiple of 4 Base64 characters
           result.append( "==" );
       }
       
       // Check if we have two byte left over
       if ( byteArrayIndex == byteArrayLength - 2 ) {
           // Convert our two bytes to an int
           byteTriplet = convertUnsignedByteToInt( data[ byteArrayIndex++ ] );
           byteTriplet <<= 8;
           byteTriplet |= convertUnsignedByteToInt( data[ byteArrayIndex++ ] );
           // Right pad the third 6 bit value with zeros
           byteTriplet <<= 2;

           byte b3 = (byte)( SIX_BIT_MASK & byteTriplet );
           byteTriplet >>= 6;
           byte b2 = (byte)( SIX_BIT_MASK & byteTriplet );
           byteTriplet >>= 6;
           byte b1 = (byte)( SIX_BIT_MASK & byteTriplet );

           result.append( mapByteToChar( b1 ) );
           result.append( mapByteToChar( b2 ) );
           result.append( mapByteToChar( b3 ) );

           // Add "==" to the output to make it a multiple of 4 Base64 characters
           result.append( "=" );           
       }

       return result.toString();
   }


   /**
    * Decode an input String using Base64
    * @param data The String to be decoded
    * @return The appropriate byte array
    */ 
   public byte[] decode( String data ) {
       mString = data;     
       mIndex = 0;
   
       /**
        * Total number of Base64 characters in the input
        */
       int mUsefulLength = 0;
       int length = mString.length();
       for( int i = 0; i < length; i++ ) {
           if( isUsefulChar( mString.charAt( i ) ) ){
               mUsefulLength++;
           }
       }       

       //mString = data;


       // A Base64 byte array is 75% the size of its String representation
       int byteArrayLength = mUsefulLength * 3 / 4;
       
       byte result[] = new byte[ byteArrayLength ];
       
       int byteTriplet = 0;        
       int byteIndex = 0;
       
       // Continue until we have less than 4 full characters left to
       // decode in the input.
       while ( byteIndex + 2 < byteArrayLength ) {
           
           // Package a set of four characters into a byte triplet
           // Each character contributes 6 bits of useful information
           byteTriplet = mapCharToInt( getNextUsefulChar() );
           byteTriplet <<= 6;
           byteTriplet |= mapCharToInt( getNextUsefulChar() );
           byteTriplet <<= 6;
           byteTriplet |= mapCharToInt( getNextUsefulChar() );
           byteTriplet <<= 6;
           byteTriplet |= mapCharToInt( getNextUsefulChar() );
           
           // Grab a normal byte (eight bits) out of the byte triplet
           // and put it in the byte array
           result[ byteIndex + 2 ] = (byte)( byteTriplet & EIGHT_BIT_MASK );
           byteTriplet >>= 8;
           result[ byteIndex + 1 ] = (byte)( byteTriplet & EIGHT_BIT_MASK );
           byteTriplet >>= 8;
           result[ byteIndex ] = (byte)( byteTriplet & EIGHT_BIT_MASK );
           byteIndex += 3;
       }
       
       // Check if we have one byte left to decode
       if ( byteIndex == byteArrayLength - 1 ) {
           // Take out the last two characters from the String
           byteTriplet = mapCharToInt( getNextUsefulChar() );
           byteTriplet <<= 6;
           byteTriplet |= mapCharToInt( getNextUsefulChar() );
           
           // Remove the padded zeros
           byteTriplet >>= 4;
           result[ byteIndex ] = (byte)( byteTriplet & EIGHT_BIT_MASK );
       }

       // Check if we have two bytes left to decode
       if ( byteIndex == byteArrayLength - 2 ) {
           // Take out the last three characters from the String
           byteTriplet = mapCharToInt( getNextUsefulChar() );
           byteTriplet <<= 6;
           byteTriplet |= mapCharToInt( getNextUsefulChar() );
           byteTriplet <<= 6;
           byteTriplet |= mapCharToInt( getNextUsefulChar() );
       
           // Remove the padded zeros
           byteTriplet >>= 2;
           result[ byteIndex + 1 ] = (byte)( byteTriplet & EIGHT_BIT_MASK );
           byteTriplet >>= 8;
           result[ byteIndex ] = (byte)( byteTriplet & EIGHT_BIT_MASK );
       }
       
       return result;
   }
   
   /**
     * Convert a Base64 character to its 6 bit value as defined by the mapping.
       * @param c Base64 character to decode
       * @return int representation of 6 bit value
       */
   private int mapCharToInt( char c ) {
       if ( c >= 'A' && c <= 'Z' ) {
           return c - 'A';
       }
       
       if ( c >= 'a' && c <= 'z' ) {
           return ( c - 'a' ) + LOWER_CASE_A_VALUE;
       }
       
       if ( c >= '0' && c <= '9' ) {
           return ( c - '0' ) + ZERO_VALUE;
       }
       
       if ( c == '+' ) {
           return PLUS_VALUE;
       }
       
       if ( c == '/' ) {
           return SLASH_VALUE;
       }
       
       throw new IllegalArgumentException( c + " is not a valid Base64 character." );
   }

   
   /**
     * Convert a byte between 0 and 63 to its Base64 character equivalent
       * @param b Byte value to be converted
       * @return Base64 char value
       */
   private char mapByteToChar( byte b ) {
       if ( b < LOWER_CASE_A_VALUE ) {
           return (char)( 'A' + b );
       }
       
       if ( b < ZERO_VALUE ) {
           return (char)( 'a' + ( b - LOWER_CASE_A_VALUE ) );
       }
       
       if ( b < PLUS_VALUE ) {
           return (char)( '0' + ( b - ZERO_VALUE ) );
       }
       
       if ( b == PLUS_VALUE ) {
           return '+';
       }
       
       if ( b == SLASH_VALUE ) {
           return '/';
       }
       
       throw new IllegalArgumentException( "Byte " + new Integer( b ) + " is not a valid Base64 value" );
   }
   
   /**
    * @param c Character to be examined
    * @return Whether or not the character is a Base64 character
    */
   private boolean isUsefulChar( char c ) {
       return ( c >= 'A' && c <= 'Z' ) ||
              ( c >= 'a' && c <= 'z' ) ||
                  ( c >= '0' && c <= '9' ) ||
                  ( c == '+' ) ||
              ( c == '/' );
   }   
       
   /**
     * Traverse the String until hitting the next Base64 character.
     * Assumes that there is still another valid Base64 character
       * left in the String.
       */
   private char getNextUsefulChar() {
       char result = '_';  // Start with a non-Base64 character
       while ( !isUsefulChar( result ) ) {
           result = mString.charAt( mIndex++ );
       }
       
       return result;
   }   
   
   /**
     * Convert a byte to an integer.  Needed because in Java bytes
       * are signed, and for Base64 purposes they are not.  If not done
       * this way, when converted to an int, 0xFF will become -127
       * @param b Byte value to be converted
       * @return Value as an integer, as if byte was unsigned
       */
   private int convertUnsignedByteToInt( byte b ) {
       if ( b >= 0 ) {
           return (int)b;
       }
       
       return 256 + b;
   }   
   
   
   
   

   public void main( String args[] ) throws Exception {
       String astring = "";
       Base64 tBase64 = new Base64();
       
       if(args.length == 2){
           if(args[0].equalsIgnoreCase("-e")){
               astring = tBase64.encode( args[1].getBytes() );
               System.out.println(astring);
           }else if(args[0].equalsIgnoreCase("-d")){
               byte result[] = tBase64.decode( args[1] );
               System.out.println(new String(result));
           }else{
               System.out.println("Useage:java Base64 -e|-d string");
           }
       }else{
               System.out.println("Useage:java Base64 -e|-d string");
       }
       
       //Test data
       //Encode data:
       //java Base64 -e "khfsuyieuiqwuie5897183274162%^$%$#$(^)&_(&*^%$%@!$#@HJHIOHKLNOIU(  UOIUJOIsdjfidd ';';';21=0\-\4324325sdfhsdfsdfgdfgregwr6e7yg9+wre/t-r/9+g6+df54g6456d4fg6+d4g564df6g4d6g4d6fg"
       //Result
       //                a2hmc3V5aWV1aXF3dWllNTg5NzE4MzI3NDE2MiVeJCUkIyQoXikmXygmKl4lJCVAISQjQEhKSElPSEtMTk9JVSggIFVPSVVKT0lzZGpmaWRkICc7JzsnOzIxPTBcLVw0MzI0MzI1c2RmaHNkZnNkZmdkZmdyZWd3cjZlN3lnOSt3cmUvdC1yLzkrZzYrZGY1NGc2NDU2ZDRmZzYrZDRnNTY0ZGY2ZzRkNmc0ZDZmZw==
       //Decode
       //java Base64 -d  a2hmc3V5aWV1aXF3dWllNTg5NzE4MzI3NDE2MiVeJCUkIyQoXikmXygmKl4lJCVAISQjQEhKSElPSEtMTk9JVSggIFVPSVVKT0lzZGpmaWRkICc7JzsnOzIxPTBcLVw0MzI0MzI1c2RmaHNkZnNkZmdkZmdyZWd3cjZlN3lnOSt3cmUvdC1yLzkrZzYrZGY1NGc2NDU2ZDRmZzYrZDRnNTY0ZGY2ZzRkNmc0ZDZmZw==
       //Result
       //                khfsuyieuiqwuie5897183274162%^$%$#$(^)&_(&*^%$%@!$#@HJHIOHKLNOIU(  UOIUJOIsdjfidd ';';';21=0\-\4324325sdfhsdfsdfgdfgregwr6e7yg9+wre/t-r/9+g6+df54g6456d4fg6+d4g564df6g4d6g4d6fg
   }
}
